/*
 * Swarm.java
 *
 * Created on January 30, 2007, 8:26 AM
 *
 * @author Greg Robinson gregarobi@dev.java.net
 */

package pso;
public class Swarm {
    
    private int nParticles; //number of member particles in swarm
    //Should be deprecated to particles.length
    
    private Particle[] particles;
    
    private double gBestFitness = 1000000000; //Global best fitness
    private double[] gBestPosition; //Position of global best
    
    //public to avoid bloating computation by accessing with get method
    public double[][] spaceSize; // [dimension][min or max]
    public int nDimensions; // number of dimensions. Should never be changed!
    public double phi1; //social factor
    public double phi2; //cognitive factor
    public double K; //Clerc's constriction factor
    
    /**
     * Creates a new instance of Swarm
     */
    public Swarm(
            int nDimensions,
            int nParticles,
            double[][] spaceSize,
            double phi1,
            double phi2){
        
        this.nDimensions = nDimensions;
        this.nParticles = nParticles;
        this.spaceSize = spaceSize;
        this.phi1 = phi1;
        this.phi2 = phi2;
        double phi = phi1 + phi2;
        this.K = 2/Math.abs(2-phi-Math.sqrt(phi*phi)-4*phi);
        
        particles = new Particle[nParticles];
        for (int i = 0; i < nParticles; i++){
            particles[i] = new Particle(this);
        }
        
        updateFitness();
    }
    
    /**
     * Accessed by subordinate Particle objects
     * @param position is a particle's position
     * @return the fitness of the particle
     */
    public static double fitness(double[] position){
        double distance = Math.sqrt(Math.pow(position[0], 2) +
                Math.pow(position[1], 2) + Math.pow(position[1], 2));
        return (-1) * Math.sin(distance)/distance;
    }
    
    /**
     * Accessed by subordinate Particle objects
     * @return the position of the global best
     */
    public double[] getGlobalBest(){
        return this.gBestPosition;
    }
    
    /**
     * Updates fitness of every particle syncronously
     */
    public void updateFitness(){
        for (int i = 0; i < this.particles.length; i++){
            double fitness = particles[i].updateFitness();
            if (fitness < this.gBestFitness){
                this.gBestFitness = fitness;
                this.gBestPosition = particles[i].getPos();
            } //end if
        } //end for
    } //end updateFitness()
    
    /**
     * Updates every particle's velocity syncronously
     */
    public void updateVelocitySync(){
        for (int i = 0; i < this.particles.length; i++){
            particles[i].updateVelocity();
        }
    }
    
    /**
     * Asynchronously pdates the fitness, position, and velocity, respectively,
     * of every particle.
     */
    public void updateParticlesAsync(){
        for (int i = 0; i < this.particles.length; i++){
            double fitness = this.particles[i].updatePosition();
            if (fitness > this.gBestFitness){
                this.gBestFitness = fitness;
                this.gBestPosition = particles[i].getPos();
            }
            this.particles[i].updateVelocity();
            
            printInfo(i);
            this.particles[i].printInfo();
        } //end for
    } //end updateParticlesAsync
    
    /**
     * Runs the swarm with asyncronous updates
     * @param maxFitness
     * @param maxIter
     * @return gBestPosition
     */
    public double[] runAsync(double maxTryIter, int maxIter){
        int iter = 0;
        int lastFind = 0;
        double lastFindValue = 10000;
        do{
            iter++;
            lastFind++;
            updateParticlesAsync();
            if (this.gBestFitness < lastFindValue){
                lastFind = 0;
                lastFindValue = this.gBestFitness;
            }
        } while (lastFind < maxTryIter && iter < maxIter);
        return gBestPosition;
    } //end runAsyc()
    
    public void printInfo(int pnum){
        System.out.println("PARTICLE: "+pnum);
        System.out.println("Global best fitness: "+gBestFitness);
        System.out.println("Global best x: "+gBestPosition[0]);
        System.out.println("Global best y: "+gBestPosition[1]);
        System.out.println("Global best z: "+gBestPosition[2]);
    }
    
} //end class Swarm
